﻿#include "./webhost.h"
#include "ui/webpage.h"
#include "ui/winview.h"
#include "utils/strutils.h"
#include "timer/timer.h"
#include "WebKit/WebKit2/WebKit2.h"
#include "WebKit/WebKit2/WKContextPrivate.h"
#include "WebKit/WebKit2/WKURLCF.h"

#include "stdio.h"

using namespace std;
using namespace MiniUI;
using namespace MiniUI::Type;

static WebHost* ToWebHost(WKViewRef webview)
{
    return (WebHost*)WKViewGetClientInfo(webview);
}

WebHost::WebHost(WebPage* pWebPage)
: m_webView(nullptr)
, m_pWebPage(pWebPage)
, m_pBytes(nullptr)
, m_isWindowActive(true)
{
    InitWebKit();
}

WebHost::~WebHost()
{
    m_pWebPage = nullptr;
    DoFinalize();
}

#pragma warning(disable:4748)  
bool WebHost::LoadURL(const char* szURL)
{
    std::wstring wstr;
    Unicode::UTF8Decode(szURL, wstr);
    CFStringRef string = CFStringCreateWithCharacters(0, (const UniChar*)wstr.data(), wstr.size());
    CFURLRef cfURL = CFURLCreateWithString(0, string, 0);
    CFRelease(string);

    WKURLRef url = WKURLCreateWithCFURL(cfURL);
    CFRelease(cfURL);

    WKPageRef page = WKViewGetPage((WKViewRef)m_webView);
    WKPageLoadURL(page, url);
    WKRelease(url);
    return true;
}
#pragma warning(default:4748)

HRESULT WebHost::SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam, bool& handled)
{
    return WKViewSendMessage((WKViewRef)m_webView, Msg, wParam, lParam, handled);
}

HRESULT WebHost::SendMessage(UINT Msg, WPARAM wParam, LPARAM lParam)
{
    bool handled = false;
    return SendMessage(Msg, wParam, lParam, handled);
}

byte* WebHost::DoPaint(const MiniRect& rcRender, Type::MiniSize& bound)
{
    SendMessage(WM_PAINT, (WPARAM)(&rcRender), 0);
    byte* pBits = (byte*)m_pBytes;    //return by OnPaintEvent
    bound = m_bound;
    m_pBytes = nullptr;
    return pBits;
}

bool WebHost::DoVisible(bool bVisible)
{
    return S_OK == SendMessage(WM_SHOWWINDOW, bVisible ? TRUE : FALSE, 0);
}

bool WebHost::DoFinalize()
{
    SendMessage(WM_CLOSE, 0, 0);
    SendMessage(WM_DESTROY, 0, 0);
    return true;
}

bool WebHost::DoFocus(bool bFocus)
{
    return S_OK == SendMessage(bFocus ? WM_SETFOCUS : WM_KILLFOCUS, 0, 0);
}

bool WebHost::DoWindowActivate(bool bActivate)
{
    m_isWindowActive = bActivate;
    return S_OK == SendMessage(WM_NCACTIVATE, 0, 0);
}

bool WebHost::DoSize(const MiniSize& size)
{
    return S_OK == SendMessage(WM_SIZE, 0, MAKELPARAM(size.cx, size.cy));
}

bool WebHost::DoMove(const MiniPoint& pt)
{
    return S_OK == SendMessage(WM_WINDOWPOSCHANGED, 0, SWP_SHOWWINDOW);;
}

bool WebHost::DoMouseDown(const MiniPoint& pt, MouseButton button, uint keyState, bool& handled)
{
    if (MB_L == button)
    {
        return S_OK == SendMessage(WM_LBUTTONDOWN, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y), handled);
    }
    else if (MB_R == button)
    {
        return S_OK == SendMessage(WM_RBUTTONDOWN, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y), handled);
    }
    else if (MB_M == button)
    {
        return S_OK == SendMessage(WM_MBUTTONDOWN, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y), handled);
    }
    return false;
}

bool WebHost::DoDblClk(const MiniPoint& pt, MouseButton button, uint keyState)
{
    if (MB_L == button)
    {
        return S_OK == SendMessage(WM_LBUTTONDBLCLK, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    else if (MB_R == button)
    {
        return S_OK == SendMessage(WM_RBUTTONDBLCLK, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    else if (MB_M == button)
    {
        return S_OK == SendMessage(WM_MBUTTONDBLCLK, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    return false;
}

bool WebHost::DoMouseUp(const MiniPoint& pt, MouseButton button, uint keyState)
{
    if (MB_L == button)
    {
        return S_OK == SendMessage(WM_LBUTTONUP, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    else if (MB_R == button)
    {
        return S_OK == SendMessage(WM_RBUTTONUP, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    else if (MB_M == button)
    {
        return S_OK == SendMessage(WM_MBUTTONUP, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
    }
    return false;
}

bool WebHost::DoMouseMove(const MiniPoint& pt, uint keyState)
{
    return S_OK == SendMessage(WM_MOUSEMOVE, (WPARAM)keyState, MAKELPARAM(pt.x, pt.y));
}

bool WebHost::DoMouseLeave()
{
    return S_OK == SendMessage(WM_MOUSELEAVE, 0, 0);
}

bool WebHost::DoMouseWheel(const MiniPoint& pt, uint keyState, int16 zDelta)
{
    return S_OK == SendMessage(WM_MOUSEWHEEL, MAKEWPARAM((UINT)keyState, (short)(zDelta * WHEEL_DELTA)), MAKELPARAM(pt.x, pt.y));
}

void WebHost::DoTimer(const Object* sender)
{
    const Timer* pTimer = (const Timer*)sender;
    SendMessage(WM_TIMER, (WPARAM)pTimer->GetTimerID(), 0);
}

void WebHost::InitWebKit()
{
    WKContextRef context = WKContextGetSharedProcessContext();

    WKViewUIClient viewUIClient = {
        nullptr,    /*set in WebView creation*/
        //API
        SetTimer,
        KillTimer,
        GetCursorPos,
        ClientToScreen,
        ScreenToClient,
        GetClientRect,
        SetCursor,
        IsWindowActive,
        GetHost,
        //Event
        OnPaintEvent,
        OnInvalidateEvent,
        OnProcessCrashEvent,
        OnRelaunchProcessEvent
    };

    m_webView = (void*)WKViewCreate(context, nullptr, this, viewUIClient);
    DoVisible(true);
}

void WebHost::SetTimer(WKViewRef webview, UINT nID, UINT nInterval)
{
    WebHost* pWebHost = ToWebHost(webview);

    Timer* pTimer = Timer::CreateInstance();
    pTimer->SetInterval(nInterval);
    pTimer->OnTimer += EventObject<OnNotifyEventFunc>(pWebHost, &WebHost::DoTimer);
    pTimer->Start(nID);
    pTimer->Release();

    pWebHost->m_timerList.push_back(pTimer);
}

void WebHost::KillTimer(WKViewRef webview, UINT nID)
{
    WebHost* pWebHost = ToWebHost(webview);
    vector<Timer*>::iterator timerIter;
    for (timerIter = pWebHost->m_timerList.begin(); timerIter != pWebHost->m_timerList.end(); ++timerIter)
    {
        Timer* pTimer = *timerIter;
        if (pTimer->GetTimerID() == nID)
        {
            pTimer->Stop();
            pWebHost->m_timerList.erase(timerIter);
            break;
        }
    }
}

void WebHost::GetCursorPos(WKViewRef webview, POINT& pt)
{
    WebHost* pWebHost = ToWebHost(webview);
    MiniPoint ptCursor;
    pWebHost->m_pWebPage->GetClientCursorPos(ptCursor);
    pt.x = ptCursor.x;
    pt.y = ptCursor.y;
}

void WebHost::ClientToScreen(WKViewRef webview, POINT& pt)
{
    WebHost* pWebHost = ToWebHost(webview);
    MiniPoint ptScreen(pt.x, pt.y);
    pWebHost->m_pWebPage->ClientToScreen(ptScreen);
    pt.x = ptScreen.x;
    pt.y = ptScreen.y;
}

void WebHost::ScreenToClient(WKViewRef webview, POINT& pt)
{
    WebHost* pWebHost = ToWebHost(webview);
    MiniPoint ptClient(pt.x, pt.y);
    pWebHost->m_pWebPage->ScreenToClient(ptClient);
    pt.x = ptClient.x;
    pt.y = ptClient.y;
}

void WebHost::GetClientRect(WKViewRef webview, RECT& rcWnd)
{
    //relative to screen, differ from UIObject::GetClientRect
    WebHost* pWebHost = ToWebHost(webview);
    MiniRect rect;
    pWebHost->m_pWebPage->GetClientRect(rect);

    if (pWebHost->m_pWebPage->GetWinView())
    {
        MiniPoint winPt;
        pWebHost->m_pWebPage->GetWinView()->GetPos(winPt);
        rect -= winPt;
    }

    rcWnd.left = rect.left;
    rcWnd.top = rect.top;
    rcWnd.right = rect.right;
    rcWnd.bottom = rect.bottom;
}

void WebHost::SetCursor(WKViewRef webview, HCURSOR newCursor)
{
    WebHost* pWebHost = ToWebHost(webview);
    pWebHost->m_pWebPage->SetCursor(CT_CUSTOM, (void*)newCursor);
    ::SetCursor(newCursor);
}

bool WebHost::IsWindowActive(WKViewRef webview)
{
    WebHost* pWebHost = ToWebHost(webview);
    return pWebHost->m_isWindowActive;
}

void* WebHost::GetHost(WKViewRef webview)
{
    WebHost* pWebHost = ToWebHost(webview);
    if (!pWebHost->m_pWebPage->GetWinView())
        return nullptr;
    else
        return pWebHost->m_pWebPage->GetWinView()->GetHost();
}

void WebHost::OnPaintEvent(WKViewRef webview, void* lpBits, int boundWidth, int boundHeight)
{
    WebHost* pWebHost = ToWebHost(webview);
    pWebHost->m_pBytes = lpBits;
    pWebHost->m_bound.SetSize(boundWidth, boundHeight);
}

void WebHost::OnInvalidateEvent(WKViewRef webview, RECT* rect)
{
    WebHost* pWebHost = ToWebHost(webview);
    if (rect)
    {
        MiniRect rcUpdate(rect->left, rect->top, rect->right, rect->bottom);
        DoCallEvent(pWebHost, OnInvalidateEvent, pWebHost->OnInvalidate, (pWebHost, &rcUpdate));
    }
    else
    {
        DoCallEvent(pWebHost, OnInvalidateEvent, pWebHost->OnInvalidate, (pWebHost, nullptr));
    }
}

void WebHost::OnProcessCrashEvent(WKViewRef webview)
{
}

void WebHost::OnRelaunchProcessEvent(WKViewRef webview)
{
}